package com.gamefx.engine1
{
	import com.gamefx.engine1.events.MapEvent;
	import com.gamefx.engine1.vo.VMaterial;
	import com.gamefx.engine1.vo.VRenderData;
	import com.gamefx.utils.ArrayEx;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;

	use namespace tm_internal;
	
	
	[Event(name = "MAP_RENDER_COMPLETED", type = "com.gamefx.engine1.events.MapEvent")]
	
	
	
	/**
	 * 游戏对象容器，是地图、战场的基类。主要功能：
	 *
	 * 渲染场面背景
	 * 管理游戏角色
	 * 镜头焦点
	 * 角色景深排序
	 *
	 * @author hzf
	 *
	 */
	public class GameContainer extends Sprite
	{
		/**
		 * 活动对象检测范围扩充常量(像素)
		 */
		public static const ACTIVITYRECT_EXTEND:int = 0;
		
		
		/**
		 * 活动对象检测范围
		 */
		tm_internal var activityRect:Rectangle;
		
		/**
		 * 标识是否需要重新开始活动游戏对象检测（用于程序优化）
		 */
		tm_internal var activityRectChanged:Boolean;
		
		/**
		 * 游戏对象层
		 */
		tm_internal var layerGameObject:Sprite;
		
		/**
		 * 游戏对象层(真实存放游戏对象的层)
		 */
		tm_internal var layerGameObjectBody:Sprite;
		
		/**
		 * 当前场景中的所有游戏对象
		 */
		tm_internal var gameObjects:ArrayEx;
		
		
		/**
		 * 显示裁剪区域
		 */
		protected var viewRect:Rectangle;
		
		/**
		 * 相对于场景显示区域
		 */
		protected var layerScrollRect:Rectangle;
		
		/**
		 * 镜头焦点可移动范围
		 */
		protected var viewBounds:Rectangle;
		
		/**
		 * 需要重新深度排序的对象列表
		 */
		protected var changeDepthObjects:Vector.<GameObject>;
		
		/**
		 * 最后一帧时间
		 */
		protected var lastTime:Number;
		
		
		
		
		
		public function GameContainer()
		{
			super();
			
			layerGameObject = new Sprite();
			layerGameObject.mouseEnabled = false;
			
			layerGameObjectBody = new Sprite();
			layerGameObject.addChild(layerGameObjectBody);
			
			gameObjects = new ArrayEx();
			changeDepthObjects = new Vector.<GameObject>();
			
			viewRect = new Rectangle();
			activityRect = new Rectangle();
			layerScrollRect = new Rectangle();
			
			_realViewPoint = new Point();
		}
		
		
		
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Properties
		//
		//--------------------------------------------------------------------------
		
		//----------------------------------
		//  renderData
		//----------------------------------
		
		protected var _renderData:VRenderData;
		
		/**
		 * 地图数据
		 * 重新设置数据后，地图会根据新的数据进行重绘。如果要销毁已经绘制的地图，只需要把mapData=null即可。
		 *
		 * @param value
		 */
		public function set renderData(value:VRenderData):void
		{
			_renderData = value;
			renderMap();
		}
		
		public function get renderData():VRenderData
		{
			return _renderData;
		}

		
		//----------------------------------
		//  viewPoint
		//----------------------------------
		
		/**
		 * 真实镜头焦点
		 */
		protected var _realViewPoint:Point;
		
		/**
		 * 镜头焦点（在RPG中，一般即为游戏主角所在坐标）
		 */
		private var _viewPoint:Point;
		
		/**
		 * 镜头焦点（在RPG中，一般即为游戏主角所在坐标）
		 * 该方法会根据各图层的相对移动速率比卷动各图层
		 *
		 * @param point 像素单位
		 *
		 */
		public function set viewPoint(point:Point):void
		{
			_viewPoint = point;
			
			calcLayerPosition();
		}
		
		public function get viewPoint():Point
		{
			return _viewPoint;
		}
		
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		
		/**
		 * 显示对象大小（地图显示区域）
		 * @param w
		 * @param h
		 */
		public function setSize(w:Number, h:Number):void
		{
			viewRect.width = layerScrollRect.width = w;
			viewRect.height = layerScrollRect.height = h;
			scrollRect = viewRect;
			activityRect.width = w + ACTIVITYRECT_EXTEND;
			activityRect.height = h + ACTIVITYRECT_EXTEND;
			
			with (graphics)
			{
				clear();
				beginFill(0x000000, 1);
				drawRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
				endFill();
			}
			
			calcViewBounds();
			calcLayerPosition();
		}
		
		/**
		 * 添加游戏对象（不显示）
		 * @param gameObject
		 */
		public function addGameObject(gameObject:GameObject):void
		{
			gameObjects.proxy[String(gameObject.data.model) + "_" + String(gameObject.data.id)] = gameObject;
			gameObject.map = this;
		}
		
		/**
		 * 根据单元id获取对应的实例对象
		 *
		 * @param GameObjectId
		 * @return
		 *
		 */
		public function getGameObjectById(gameObjectId:String):GameObject
		{
			if (gameObjects.proxy.hasOwnProperty(gameObjectId))
			{
				return gameObjects.proxy[gameObjectId] as GameObject;
			}
			else
			{
				return null;
			}
		}
		
		/**
		 * 删除单元
		 *
		 * @param GameObjectId
		 *
		 */
		public function removeGameObjectById(gameObjectId:String):void
		{
			if (gameObjects.proxy.hasOwnProperty(gameObjectId))
			{
				var gameObject:GameObject = gameObjects.proxy[gameObjectId] as GameObject;
				hideGameObject(gameObject);
				
				gameObject.destory();
				delete gameObjects.proxy[gameObjectId];
			}
		}
		
		/**
		 * 显示游戏对象
		 * @param gameObject
		 */
		public function showGameObject(gameObject:GameObject):void
		{
			layerGameObjectBody.addChild(gameObject);
			updateObjectDepth(gameObject);
		}
		
		/**
		 * 隐藏游戏对象
		 * @param gameObject
		 */
		public function hideGameObject(gameObject:GameObject):void
		{
			if (layerGameObjectBody.contains(gameObject))
				layerGameObjectBody.removeChild(gameObject);
		}
		
		/**
		 * 是否在显示范围内
		 * @param x
		 * @param y
		 * @return 
		 * 
		 */
		public function viewRectHitTest(x:Number, y:Number):Boolean
		{
			return layerScrollRect.contains(x, y);
		}


		
		/**
		 * 更新材质层
		 */ 
		public function updateMaterials():void
		{
			while (numChildren)
			{
				removeChildAt(0);
			}
			
			_renderData.materials.sort(compareMaterial);
			
			var material:VMaterial;
			var depth:uint = 0;
			
			for each (material in _renderData.materials)
			{
				addChild(material.image);
				if (material.index <= 0)
				{
					depth++;
				}
			}
			
			addChildAt(layerGameObject, depth);
			
			calcLayerPosition();
		}
		
		/**
		 * 销毁地图，释放内存
		 *
		 */
		public function destroy():void
		{
			removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
			
			if (layerGameObject.parent)
			{
				removeChild(layerGameObject);
			}
			
			var gameObject:GameObject;
			while (gameObjects.length > 0)
			{
				gameObject = gameObjects[0];
				removeGameObjectById(gameObject.data.model + "_" + gameObject.data.id);
			}
			
			gameObjects = new ArrayEx();
			changeDepthObjects.length = 0;
			
			while (numChildren)
			{
				removeChildAt(0);
			}
		}
		
		/**
		 * 将对象添加到景深重新排序队列
		 * @param gameObject
		 *
		 */
		tm_internal function updateObjectDepth(gameObject:GameObject):void
		{
			changeDepthObjects.push(gameObject);
		}
		

		
		/**
		 * 根据地图数据渲染地图
		 *
		 * @param mapData
		 *
		 */
		protected function renderMap():void
		{
			destroy();
			
			if (_renderData == null)
			{
				return;
			}
			
			calcViewBounds();
			updateMaterials();
			
			lastTime = getTimer();
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			dispatchEvent(new MapEvent(MapEvent.MAP_RENDER_COMPLETED));
		}
		
		/**
		 * 计算各图层的位置
		 *
		 */
		protected function calcLayerPosition():void
		{
			if (_viewPoint == null)
			{
				return;
			}
			
			_realViewPoint.x = _viewPoint.x;
			_realViewPoint.y = _viewPoint.y;
			
			if (_realViewPoint.x < viewBounds.left)
			{
				_realViewPoint.x = viewBounds.left;
			}
			else if (_realViewPoint.x > viewBounds.right)
			{
				_realViewPoint.x = viewBounds.right;
			}
			if (_realViewPoint.y < viewBounds.top)
			{
				_realViewPoint.y = viewBounds.top;
			}
			else if (_realViewPoint.y > viewBounds.bottom)
			{
				_realViewPoint.y = viewBounds.bottom;
			}
			
			layerGameObject.x = viewBounds.left - _realViewPoint.x;
			layerGameObject.y = viewBounds.top - _realViewPoint.y;
			
			layerScrollRect.x = -layerGameObject.x;
			layerScrollRect.y = -layerGameObject.y;
			
			activityRect.x = -layerGameObject.x - ACTIVITYRECT_EXTEND;
			activityRect.y = -layerGameObject.y - ACTIVITYRECT_EXTEND;
			
			activityRectChanged = true;
			
			
			var material:VMaterial;
			for each (material in _renderData.materials)
			{
				material.image.x = material.x + int(layerGameObject.x * material.xspeed);
				material.image.y = material.y + int(layerGameObject.y * material.yspeed);
			}
		}
		
		/**
		 * renderData.materials 的排序函数
		 * 
		 * @param a
		 * @param b
		 * @return 负数，则a排在b之前
		 */
		private function compareMaterial(a:VMaterial, b:VMaterial):Number
		{
			return a.index - b.index;
		}
		
		/**
		 * 当地图舞台大小改变或地图数据重载时，重新计算镜头移动范围
		 *
		 */
		private function calcViewBounds():void
		{
			if (_renderData == null)
			{
				return;
			}
			
			viewBounds = new Rectangle(viewRect.width >> 1, viewRect.height >> 1, _renderData.width - viewRect.width, _renderData.height - viewRect.height);
		}

		
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		/**
		 * @param event
		 *
		 */
		protected function enterFrameHandler(event:Event = null):void
		{
			var currTime:int = getTimer();
			var dt:int = currTime - lastTime;
			//			if (dt < 83) return;
			lastTime = currTime;
			
			var gameObject:GameObject;
			var len:uint = gameObjects.length;
			var i:uint;
			for (i = 0; i < len; i++)
			{
				gameObject = gameObjects[i] as GameObject;
				gameObject.enterFrameHandler(null, dt);
			}
			
			//深度排序
			if (changeDepthObjects.length > 0)
			{
				var depth:int, index:int, tmpObject:GameObject;
				var oLen:int = layerGameObjectBody.numChildren;
				var cLen:uint = changeDepthObjects.length;
				for (i = 0; i < cLen; i++)
				{
					gameObject = changeDepthObjects[i];
					if (!gameObject.isActivity)
					{
						continue;
					}
					
					depth = 0;
					while (depth < oLen)
					{
						tmpObject = layerGameObjectBody.getChildAt(depth) as GameObject;
						if (tmpObject && gameObject.depth < tmpObject.depth)
						{
							break;
						}
						++depth;
					}
					
					if (layerGameObjectBody.contains(gameObject))
					{
						index = layerGameObjectBody.getChildIndex(gameObject);
						if (depth >= index)
						{
							--depth;
						}
						layerGameObjectBody.setChildIndex(gameObject, depth);
					}
				}
				
				changeDepthObjects.length = 0;
			}

			activityRectChanged = false;
		}
		
	}
}